{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.5.2"
    },
    "colab": {
      "name": "vbpr_text.ipynb",
      "provenance": []
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uxfPR6vG5GP0",
        "colab_type": "text"
      },
      "source": [
        "*Copyright (c) Cornac Authors. All rights reserved.*\n",
        "\n",
        "*Licensed under the Apache 2.0 License.*\n",
        "\n",
        "# Visual Bayesian Personalized Ranking with Text Data"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "G0R8gyyt5GP4",
        "colab_type": "text"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/PreferredAI/cornac/blob/master/tutorials/vbpr_text.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/PreferredAI/cornac/blob/master/tutorials/vbpr_text.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TURsVngV5GP5",
        "colab_type": "text"
      },
      "source": [
        "## Overview\n",
        "\n",
        "We would like to use [Visual Bayesian Personalizer Ranking (VBPR)](https://arxiv.org/pdf/1510.01784.pdf), the model makes use of pre-trained visual features extracted from CNN. However, our data of interest [MovieLens dataset](https://grouplens.org/datasets/movielens/) does not come with visual information, but instead it contains text movie plots. In this tutorial, we will employ Conac's modality infrastructures to easily utilize VBPR to leverage item text content.\n",
        "\n",
        "## Setup"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "d7087AnL5Jte",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# install Cornac and PyTorch (VBPR model implementation uses PyTorch)\n",
        "!pip3 install cornac torch>=0.4.1"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "8e1edON-5GP7",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        },
        "outputId": "f609c795-cadb-45d5-fab1-25a0879853ff"
      },
      "source": [
        "import cornac\n",
        "from cornac.data import Reader\n",
        "from cornac.datasets import movielens\n",
        "from cornac.eval_methods import RatioSplit\n",
        "from cornac.data import TextModality, ImageModality\n",
        "from cornac.data.text import BaseTokenizer\n",
        "\n",
        "print(\"Cornac version: {}\".format(cornac.__version__))"
      ],
      "execution_count": 2,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Cornac version: 1.4.0\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "H8OadBhB5GQE",
        "colab_type": "text"
      },
      "source": [
        "## Prepare data\n",
        "Here we use the MovieLens 100K dataset which is already accessible from Cornac. Hence, we can simply load movie plots and the rating data."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "bP9jY6dl5GQF",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "plots, movie_ids = movielens.load_plot()\n",
        "\n",
        "# movies without plots are filtered out by `cornac.data.Reader`\n",
        "ml_100k = movielens.load_feedback(reader=Reader(item_set=movie_ids))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "43wktekd5GQJ",
        "colab_type": "text"
      },
      "source": [
        "## Cross modality\n",
        "\n",
        "To get vector representations from text data, we build a `TextModality` using our corpus and corresponding ids. We also need to supply a `Tokenizer` for text splitting, in this case tokens are seperated by `\\tab` character. We limit the maximum size of vocabulary to 5000, which also means the dimension of our vector space cannot go higher."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "0VHNg6tm5GQK",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "item_text_modality = TextModality(corpus=plots, ids=movie_ids, \n",
        "                                  tokenizer=BaseTokenizer(sep='\\t', stop_words='english'),\n",
        "                                  max_vocab=5000, max_doc_freq=0.5).build()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xGYc5L7e5GQS",
        "colab_type": "text"
      },
      "source": [
        "Next step is to create an `ImageModality`, which is use by VBPR, using our text representations. In this case, we take the word-count matrix to substitute for visual features."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "gUGJ5AjI5GQU",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "features = item_text_modality.count_matrix.A\n",
        "item_image_modality = ImageModality(features=features, ids=movie_ids)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Rkxw-D_m5GQY",
        "colab_type": "text"
      },
      "source": [
        "In Cornac, every model relies on the modality for which it was designed for (i.e., visual recommendation algorithms always work with `ImageModality`). This ensures consistency with models' original assumptions, and helps us avoid confusions regarding which modality to use when integrating a new recommender model.\n",
        "\n",
        "## Experiment\n",
        "\n",
        "We employ the `RatioSplit` evaluation method to split the rating data. The `item_image_modality` is also supplied here for later usage by the model."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "cc66mPy35GQZ",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "ratio_split = RatioSplit(data=ml_100k, test_size=0.9,\n",
        "                         item_image=item_image_modality,\n",
        "                         exclude_unknowns=True, \n",
        "                         verbose=True, seed=123)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bKalGw5x5GQe",
        "colab_type": "text"
      },
      "source": [
        "We are now ready to evaluate performance of VBPR. The [BPR](https://arxiv.org/ftp/arxiv/papers/1205/1205.2618.pdf) model is also included as a baseline to examine the effectiveness of the text auxiliary data."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "rczsfDrO5GQg",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "vbpr = cornac.models.VBPR(k=10, k2=10, n_epochs=20, batch_size=10, learning_rate=0.001,\n",
        "                          lambda_w=1.0, lambda_b=0.0, lambda_e=100.0, use_gpu=True, seed=123)\n",
        "\n",
        "bpr = cornac.models.BPR(k=10, max_iter=100, learning_rate=0.001, lambda_reg=0.001, seed=123)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "giDcupSL5GQk",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "auc = cornac.metrics.AUC()\n",
        "rec_50 = cornac.metrics.Recall(k=50)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "yes6tv-15GQo",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "cornac.Experiment(eval_method=ratio_split,\n",
        "                  models=[bpr, vbpr],\n",
        "                  metrics=[auc, rec_50]).run()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "_Nbplrac5GQs",
        "colab_type": "text"
      },
      "source": [
        "Results after running the experiment:\n",
        "\n",
        "<pre>\n",
        "TEST:\n",
        "...\n",
        "     |    AUC | Recall@50 | Train (s) | Test (s)\n",
        "---- + ------ + --------- + --------- + --------\n",
        "BPR  | 0.8073 |    0.2301 |    0.2390 |   1.1167\n",
        "VBPR | 0.8219 |    0.2519 |  113.8606 |   1.0624\n",
        "</pre>"
      ]
    }
  ]
}